home *** CD-ROM | disk | FTP | other *** search
- ####################################################################
- # TapeLibrary.perl #
- # #
- # This set of routines handles tar files to tape in a flexible #
- # and useful manner, doing a lot of error checking along the way. #
- # It includes: #
- # Interfacing to the mt tape movement commands. #
- # Rudimentary tape ownership. #
- # Status checking and error recovery. #
- # Creation of labeled backup logs in the users directory. #
- # #
- # To use this library you will need: #
- # Perl 4.10 or higher. #
- # gnutar 1.10 or higher. #
- # #
- # Next binaries & source for mtsense are included. #
- # #
- # The library has a few things hardwired in that you will need to #
- # customize to your setup. e.g. which hosts have DAT drives, which #
- # have exebyte drives. #
- # #
- # It was originally designed for use on Nexts. If porting to other #
- # machines, you will need to modify TapeStatus, which parses the #
- # output of mt status. You will also have to recompile mtsense #
- # for your platform. #
- # #
- # This file can be located anywhere, but the scripts assume #
- # a default location of /usr/local/lib/perl. #
- # #
- # #
- ####################################################################
-
-
- ####################################################################
- # Set up Global variables #
- # and initialize drive. #
- ####################################################################
-
- $true = 1;
- $false = 0;
-
- # reset is used to keep track of error recovery status when
- # there is a SCSI bus reset.
- $reset = $false;
-
- # tabcount is for spacing in the debug display routine Say
- $tabcount = 0;
-
- # TapeMode is either read or write. Determines certain actions
- # in error recover.
- $Read = $false;
- $Write = $true;
- $TapeMode = $Read;
-
- # tarnum is the number of the current tar we're working on.
- $tarnum = 0;
-
- # gtar is the command used for taring. If not on your standard
- # path, fill in path here.
- $gtar= "gnutar";
-
- # mtsense is the command used to set drive controller and drive
- # to the chosen mode for that drive.
- $mtsense = "/u/arafel/adm/backup/mtsense";
-
-
- # Turn flush on (Output from script and from external commands
- # seems to appear out of sequence sometimes)
-
- select(STDERR); $|=1;
- select(STDOUT); $|=1;
-
- # Create a datestring of the form dd-MON-yy.
- # This datestring is used as part of the name of all log files.
-
- ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
- $MonOfYear = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
- $DateString = $mday."-".$MonOfYear."-".$year;
-
- # A seperate routine Say checks the value of DEBUG. If it's YES
- # then Say prints it's message, indented according to the depth of
- # the subroutine call. Otherwise it says nothing.
-
- $DEBUG= $ENV{DEBUG};
-
- # Customize:
- # nrxt0 is the non-rewind exabyte driver. nrst0 for other scsi
- # tape drives. We have both, so we first check the environment
- # variable TAPE. If it's blank, then we make the decision on the
- # basis of what host we're running on. The use of the environment
- # variable makes it easy to test additional drivers.
- #
-
- $TAPE = $ENV{TAPE};
- if ("$TAPE" eq "") {
- if (`/bin/hostname` eq "arafel\n" ) { $TAPE = "/dev/nrst0";}
- else { $TAPE ="/dev/nrxt0"; }
- }
- #print "TapeLibrary thinks it should use tape device $TAPE \n";
-
- # TapeMessage is a global variable that is set to the decimal
- # SCSI SenseKey byte by any routine that moves the tape.
-
- # ErrorCode is set by the various routines that verify the label
- # and permissions for writing on the tape.
-
- $TapeMessage = 0;
- $ErrorCode = 0;
-
- # Since the previous tape operation may have ended with an error
- # we call TapeStatus twice so that we know what state the drive is in NOW
-
- &TapeStatus; &TapeStatus; #clear previous error conditions
-
- # Not being very trusting, we will set the tape controller
- # AND the drive to specified format.
-
- &SetupDrive;
-
-
- ####################################################################
- # #
- # sub SetupDrive #
- # Sets the driver to fixed blocks of 8192, #
- # and calls a mode select to set the drive too. #
- # This routine needs to be called every time the drive resets #
- # or the host reboots. Thus it is called on startup and on #
- # certain error recoveries #
- # #
- ####################################################################
- # Customize:
- # I decided on a common format of 8192 bytes, fixed block as being the
- # easiest to maintain for portability. You may decide to format your
- # tapes differently.
-
- sub SetupDrive {
- $tabcount++;
- &Say("Setting Tape Block Size");
- local(@message);
- @message = `$mtsense -b 8192 `;
- $mtreturn = $?;
- print @message, $mtreturn, "\n";
- $mtreturn = $?;
- &TapeStatusTrans(&TapeStatus);
- &Say("Setting tape driver to fixed blocks of size 8192");
- $tabcount--;
- }
-
-
- ####################################################################
- # sub TapeReady #
- # #
- # It seems to be easy to get Exabyte tape #
- # drives confused. TapeReady has evoloved through many #
- # incarnations before settling into this form. #
- # #
- # TapeReady depends on a global variable set by #
- # TapeStatus: This is TapeMessage, which is the SCSI #
- # sense key byte. #
- # #
- # TapeReady calls TapeStatus, looks at the output, and #
- # decides whether the drive is ready to go to work, if it's #
- # still busy with a previous command, or if the device #
- # glitched, and has reset, in which case it's state is #
- # unknown #
- # #
- # Tapeready respectively returns, waits, and #
- # resets the tape position in each of these three #
- # circumstances. #
- # #
- ####################################################################
- sub TapeReady {
- $tabcount++;
- &Say("Tapeready...");
- $TRcount=0;
- do {
- &TapeStatusTrans(&TapeStatus);
- if ($TapeMessageStat == 8) {
- &Say("Drive is still busy with previous command.");
- # I've yet to get one of these. Next ioctl
- # intercepts and returns on standard error.
- }
-
- if (($TapeMessage & 6) == 6) {
- # message: device reset.
- # This typically will occur about 5% of the time.
- # It has caused me an unreal amount of grief
- # as the current command is aborted, and
- # the tape rewound.
- &Say("TapeReady: Device Reset while executing");
- if ($reset >= 0 ) {&ResetTapePosition;}
- $tabcount--;
- return;
- }
-
- sleep 5 * $TRcount++;
- # sleeps start at 0 and increase in steps of 5. Some
- # drives get unhappy if they receive a command while
- # resetting. With this, eventually the sleep gets long
- # enough to work. You may wish to change the 5 to a higher
- # number if you are getting repeated waits.
- if ($TapeMessage == 512 ) { #i/o error (device is busy)
- print "...waiting ";
- }
- if ($TRcount == 20) {# Probably no tape in drive
- # You want this to time out eventually. I put a tape in
- # one day to do a test. Two copies of the program running
- # from the weekend tried to grab the tape out from under me.
- print "Tape drive is not ready after $TRcount tries\n";
- exit(37); # 37?? Seemed that odd behaviour deserves odd numbers
- }
- } until (($TapeMessage & 15) == 0) && ($TapeMessage < 256);
- # Lower 4 bits of $TapeMessage report errors. Top 4 bits are flags
- # indicating tape position and such. If mt status returned on stderr
- # TapeMessage is 256 * error returned.
- &Say("Tapeready...succeeded");
- $tabcount--;
- return;
- }
-
- ####################################################################
- # sub TapeStatus #
- # TapeStatus parses the output of mt status. If the tape #
- # drive is available, it sets $TapeMessage to be the SCSI #
- # sense code. It can also return stuff on standard error #
- # if the device is allocated by another process. Or there #
- # can be an io error. These return 256 and 512 respectively #
- # as their error code. #
- ####################################################################
-
-
- sub TapeStatus {
- $tabcount++;
- &Say("TapeStatus...");
- # Uncomment following three lines for thorough diagnostics
- # print "TapeStatus Diagnostic: @message\n";
- # Note: mt status clears any error conditions.
- local(@mtsmessage) =` $mtsense -rm `;
- local(@message) = ` mt -f $TAPE status 2>&1`;
- #print(@message, "\n");
- local ($mtreturn) = $?;
- ($dev,$device,$busy) = split(/\s+/,$message[0]);
- if (($busy eq "busy")||
- ($message[0] eq "$TAPE: No such device or address\n")){
- # Drive is in use or tied up by some other program.
- #print "\tTapeStatus: busy $busy dev $dev \n";
- print $message[0];
- $TapeMessage=256;
- }
- elsif ("$dev" eq "${TAPE}:" ){ # error of the form /dev/tape: i/o error
- $TapeMessage=512;
- }
- else {
- ($t,$s,$r,$SenseKey) = split(/\s+/,$message[1]);
- ($t,$s,$r,$TapeMessage) = split(/\s+/,$message[2]);
- $TapeMessage = hex(substr($SenseKey,2,2));
- $TapeMessageStat = hex(substr($SenseCode,2,2));
- if (($TapeMessage & 15) != 0) {
- &Say("\nTapeStatus Found Error:", `/bin/date`);
- &Say(`/bin/date`);
- print @message, "\n\n";
- #print @mtsmessage,"\n\n";
- }
- }
- &Say("Sense Byte: $TapeMessage \tSense Status: $TapeMessageStat.");
- &Say("TapeStatus... exited");
-
- $tabcount--;
- return ($TapeMessage);
-
- }
-
- ####################################################################
- # sub TapeStatusTrans #
- # #
- # This takes the output of TapeStatus and translates it #
- # back into English. #
- # #
- # Error 512 has usually occured when Next's scsi #
- # controller issued a device reset. This is a major hassle, #
- # as it automatically rewinds the tape, and restores the #
- # drive to poweron defaults. This should cause the program #
- # to exit, but it happens so often, that I've gone to great #
- # lengths to keep track of the state of the drive, and now, it #
- # will reset the tape to the start of that file and try again. #
- # #
- # Error 256 has been associated with the device being taken #
- # by another process. This is reason for immediate exit. #
- # #
- # The sense key byte is broken into two sections: The first 3 #
- # bits are flags, giving details about the tape location. #
- # (At a filemark, at beginning of tape, and so on) These #
- # first 3 can be set without an error occuring. The last 5 #
- # bits are flags that result in a check condition status #
- # being issued by the drive. #
- # #
- # Because any combination of flags can exist, #
- # TapeStatusTrans checks each flag in turn, prints the #
- # appropriate message, and decrements the byte. It could #
- # be done with masks, but this seemed more transparent. #
- # #
- # Ones that say, #
- # "Your Software is out of date" #
- # indicate that this error has no translatation in the #
- # Exabyte programming guide. #
- # #
- ####################################################################
- sub TapeStatusTrans {
- local($code) = $_[0];
- $tabcount++;
- &Say("TapeStatusTranslate...");
- if ($code == 0) { &Say("\tTapeStatus OK");}
- if ($code >= 512) {
- print " Next\'s tape driver screws up again! \n ";
- $code -= 512;
- }
- if ($code >= 256) {
- print "\tCome again later. Someone else has glommed onto the drive. \n";
- exit;
- }
- if ($code >= 128) {
- print "\tTape is at a file mark \n";
- $code-=128;
- }
- if ($code >= 64) {
- print "\tTape is at Logical Begining or End of Tape \n";
- $code-=64;
- }
- if ($code >= 32) {
- print "\tData request length doesn't equal logical block length \n";
- $code-=32;
- }
- if ($code >= 16) {
- print "\tReserved Bit. This software is out of date. \n";
- $code-=16;
- }
- if (($code == 0) && $diag ){
- print "\tNo Additional Data to report. All is well. \n";
- }
- if ($code == 1) {
- print "\tThis Software is out of date. \n";
- }
- if ($code == 2) {
- print "\tDrive not ready. Check drive for tape, and close door. \n";
- }
- if ($code == 3) {
- print "\tUnrecoverable medium error. Buy a new gypsy. \n";
- # Ok, I'm being flippent. This is either a bad tape, or the drive
- # and the driver aren't agreeing on blocking.
- }
- if ($code == 4) {
- print "\tHardware error. Check your warranty. \n";
- }
- if ($code == 5) {
- print "\tIllegal Request. (Wrong mode, ill-formed, or bad parameter) \n";
- }
- if ($code == 6) {
- print "\tUnit Attention. (Cartridge change or device reset) \n";
- # Next and I are trying to find the source of this bug. It happens
- # quite regularly on opening a new file on the non-rewind device.
- }
- if ($code == 7) {
- print "\tCartridge is write protected. \n";
- }
-
- if ($code == 8) {
- print "\tTape is blank, or is at end of data. \n";
- }
- if ($code == 9) {
- print "\tExabyte specific: Tape mark detect error or Transfer Abort.\n";
- }
- if ($code == 10) {
- print "\tYour software is out of date.\n";
- }
- if ($code == 11) {
- print "\tAborted Command.\n";
- }
- if ($code == 12 ) {
- print "\tYour software is out of date.\n";
- }
- if ($code == 13) {
- print "\tVolume Overflow. You ran off the end of the tape. \n";
- }
- if ($code == 14 ) {
- print "\tYour software is out of date.\n";
- }
- if ($code == 15 ) {
- print "\tYour software is out of date.\n";
- }
- &Say("TapeStatusTranslate...exited");
- $tabcount--;
- }
-
-
-
- # sub RewindTape
- #
- # RewindTape is called for two reasons. It's called at the
- # beginning of the script in the event that the script
- # glitched and is restarting itself. It's also called when
- # the program gets confused as to where the tape is. If this
- # is the result of a device reset, then the tapedrive will
- # not recognize commands until it finishes its reset. Thus
- # RewindTape, will try repeatedly until mt rewind returns
- # 0, or until it times out.
- #
- # Note that sending the tape drive commands while it's
- # resetting can be upsetting, and cause a rereset.
- # (re-squared-set?) Thus, the progressively longer waits
- # between retries.
- #
- # Note also, that mt rewind can return before the tape is
- # actually rewound. Hence the call to TapeReady *after*
- # the execution of mt. Most of the other tape commands only
- # call it before.
-
-
-
- sub RewindTape {
- $tabcount++;
- &Say("RewindTape...");
- local (@message, $mtreturn );
- &TapeReady($reset);
- # a bunch of odd conditions could make it other than 64 and the End of
- # tape, so we make a strict compare. May result in rewinding when it's
- # already rewound. No harm. Just wasted time.
- if ($TapeMessage == 64 ) {
- print "\t\tTape is already rewound. \n";
- &Say("RewindTape...exited");
- $tabcount--;
- return(0);
- }
- else {
- print "\t\tExecuting rewind sequence \n";
- $count = 0;
- do {
- @message = `/bin/mt -f $TAPE rewind`;
- $mtreturn = $?;
- if ($mtreturn != 0) {
- system("/bin/date");
- print "\tRewind attempt #$count failed:\n";
- &ErrorTranslate(110);
- print "\t\t mtreturn $mtreturn \n";
- sleep( 5 * $count);
- }
- if ($count++ >= 10) {
- print "\tRewind failed after many attempts. Exiting \n";
- exit;
- }
- &TapeReady($reset);
- } until $mtreturn == 0 ;
- }
-
- &Say("RewindTape...exited");
- $tabcount--;
- return(0);
- }
-
- # sub ForwardSpaceTape
- #
- # Moving a tape forward positions it after the next
- # tapemark. Unless the end of the tape gets in the way. On the
- # Exabyte, you cannot fsf across an EOD marker. You can fsf
- # from an EOD marker. This rather strange behaviour can get
- # one in trouble, so we checkfor that.
-
-
- sub ForwardSpaceTape {
- $tabcount++;
- local(@message,$mtreturn,$Marks);
- $Marks = $_[0]; $mtreturn = 0;
- &Say("ForwardSpaceTape...$Marks");
- if ($Marks >= 0){
- &TapeStatus;
- if (($TapeMessage & 8) != 8) { #tape is not at EOD/blank
- print "\t\tMoving tape forward past $Marks Tapemarks.\n";
- &TapeReady($reset);
- foreach (1..$Marks) {
- @message = `/bin/mt -f $TAPE fsf 2>&1`;
- }
- $mtreturn = $?;
- if ($mtreturn != 0 ) {
- system("/bin/date");
- print "\t***ForwardSpaceTape: @message";
- $CurrentStatus = &TapeStatus;
- &TapeStatusTrans($CurrentStatus);
- print "\t******Failed to move tape forward. \n";
- print "\t******mtreturn $mtreturn \n";
- #&ResetTapePosition;
- print @message;
- exit;
- }
- }
- }
- $ErrorCode = 200 + $mtreturn;
- &Say("ForwardSpaceTape...exited");
- $tabcount--;
- return($ErrorCode);
-
- }
-
-
- # sub BackSpaceTape
- #
- # Backspacing a tape can be done anywhere-- except at the
- # beginning of the tape. The verification is done in the
- # event that a Reset occurs during a rewind.
- #
-
- sub BackSpaceTape {
- $tabcount++;
- local(@message,$mtreturn,$marks);
- $Marks = $_[0];
- &Say("BackSpaceTape...$Marks");
- &TapeReady($reset);
- if (($TapeMessage & 64) != 64) { #tape is not at BOT
- print "\t\tMoving tape back past $Marks Tapemarks.\n";
- @message = `/bin/mt -f $TAPE bsf $Marks 2>&1`;
- $mtreturn = $?;
- if ($mtreturn != 0) {
- system("/bin/date");
- print "\t***Failed to move tape back: \n";
- print "\t***BackSpaceTape: @message";
- print " mtreturn $mtreturn \n";
- &TapeStatusTrans(&TapeStatus);
- if (($TapeMessage & 6) == 6) {
- &ResetTapePosition;
- &TapeStatusTrans(&TapeStatus);
- }
- }
- }
- else {
- &Say("**Tape is at beginning of tape. Can't backspace");
- }
- $ErrorCode = 200 + $mtreturn;
- &Say("BackSpaceTape...exited");
- $tabcount--;
- return($ErrorCode);
- }
-
- # WriteMark expects tape to be positioned before an EOF. This is
- # done by gnutar reading. It is not done by a mt fsf. If tape
- # is positioned with a fsf, issue a bsf before running this command.
- #
- # Exabyte tapes will not write except on an eof, or on blank tape. Thus
- # to overwrite, but still leave a mark on the tape, one must write TWO file
- # marks, then backup to the spot between them.
- #
- # A failure of WriteMark should cause the script to abort.
- #
- sub WriteMark {
- $tabcount++;
- &Say("WriteMark $_[0] ...");
- local (@message, $mtreturn,$Marks);
- $Marks = $_[0];
- &TapeReady($reset);
- print "\t\tWriting $Marks Tapemarks \n";
- @message = `/bin/mt -f $TAPE weof $Marks`;
- $mtreturn = $?;
- if ($mtreturn == 0) {$ErrorCode =200;} else {$ErrorCode =111;}
- &Say("WriteMark...exited");
- $tabcount--;
- return($ErrorCode);
- }
-
-
- # sub EraseRestOfTape
- #
- # Again, this takes account of the way both Exabyte's and
- # DATs deal with filemarks. A program can only start
- # writing at the BOT side of a filemark, or on blank tape. If
- # restarting a command, you want to overwrite a part of the
- # file, but you also want to leave a filemark at the end of the
- # previous file. The way to solve this is to write two
- # filemarks, then backspace over 1 of them. This is what
- # EraseRestOfTape does.
- #
- # Note: An fsf, then another fsf will move past an
- # EraseRestOfTape, leaving the tape at the filemark
- # following the Erase. This gets very messy, and is beyond
- # the scope of this program.
- #
-
-
-
- sub EraseRestOfTape {
- $tabcount++;
- &Say("EraseRestOfTape ...");
- print "\t\tErasing from Here to end of tape. \n";
- &WriteMark(2);
- if ($ErrorCode = 200) { # proceed only if ok
- &BackSpaceTape(1);
- }
- &ErrorTranslate($ErrorCode);
- &Say("EraseRestOfTape ... exited");
- $tabcount--;
- return($ErrorCode);
- }
-
- sub Offline {
- $tabcount++;
- &Say("Offline ...");
- local (@message);
- &TapeReady($reset);
- print "\tEjecting tape...\n";
- @message = `/bin/mt -f $TAPE offline`;
- print @message;
- #&TapeStatusTrans(&TapeStatus);
- &Say("Offline ...exited");
- $tabcount--;
- }
-
-
-
-
- # sub CheckVolumeLabel
- #
- # The heart of the tapelabeling system is here: When a tape
- # is labeled, a 0 length file is created in
- # /usr/local/tapelabels. This file is owned by the person who ran
- # the tape labeler. /etc/tapelables is public writable,
- # but the sticky bit is set, so only a file's owner can delete
- # it. After this file is created, it is tarred onto the tape.
- #
- # CheckVolumeLabel reads the tarfile, getting just the
- # name. It then checks that this name is registered, that
- # the program user id and the owner of the file are indeed the
- # same. It returns with an error if the tape is either
- # unlabeled, is labeled, but not registered, or if the ID's
- # don't match.
- #
- # Note that an error is returned even for root. This
- # prevents the nightly backup script from clobbering a
- # user who left his tape in the drive.
- #
- # This system (at present) does not stop users from using
- # the tape directly.
- #
-
-
- sub CheckVolumeLabel {
- local( $label);
- $tabcount++;
- &Say("CheckVolumeLabel ...");
- &RewindTape;
- &TapeReady($reset);
- chop ($label=`$gtar -tb 16 -f $TAPE`);
- &TapeStatus; # Clear residual errors from tape.
- #
- # Check the label
- #
- $LabelErrorCode = 100; # No Errors found
- # tape is unlabeled
- if ( -d "/usr/local/tapelabels/$label" ) {
- $LabelErrorCode = 102;
- } # label is null, hence checking /usr/local/tapelabels, a directory.
-
- elsif ( ! -e "/usr/local/tapelabels/$label" ) {
- $LabelErrorCode = 103;
- } # tape isn't registered
-
- elsif ( ! -o "/usr/local/tapelabels/$label" ) {
- $LabelErrorCode = 104;
- } # tape is not owned by user
- &TapeStatus; # Clear residual errors from tape.
- &Say("CheckVolumeLabel ...exited");
- $tabcount--;
- return($label);
- }
-
-
- # sub TarDir
- #
- # This is one of two routines that actually do anything.
- # Tardir expects to be passed the full directory name of the
- # directory to archive. It will then cd to the parent
- # directory of this directory, and make the archive
- # storeing the relative path from that point.
- #
- # Since gnutar has provision for labeling an archive, we
- # make a label of the form Tapename-Dirname-Date. This can
- # be used to rebuild the files in the backups directory if
- # they are lost.
- #
- # Various things can go wrong, so TarDir only tries a finite
- # number of times. It also times the run, and figures the
- # transfer rate. If this transfer rate is below the minimum
- # streaming rate for the drive, the drive will fill in with
- # blank tracks. This reduces the capacity of the drive.
-
-
- sub TarDir {
- $tabcount++;
- $tarnum++;
- &Say("TarDir...");
- local($FullDirName,$DirName,$Lbl,$gtreturn,$TarCount);
- $TapeMode=$Write;
- ($FullDirName) = @_;
- $TarCount = 0;
- $DirName = `basename $FullDirName`;
- chop $DirName;
- $Lbl = $tapelabel."-".$DirName."-".$DateString;
- print "Archive Label is $Lbl \n";
- chdir($FullDirName);
- chdir "..";
- TarLoop:
- do {
- &TapeReady($reset);
- $timestart=time;
- &Say("Tardir: tarring:");
- @gtmessage =`$gtar -c -b 16 -f $TAPE -V $Lbl -L 1250000 +totals $DirName 2>&1`;
- $gtreturn=$?;
- $timestop=time;
- $tartime=$timestop-$timestart;
- #!&TapeStatusTrans($TarTapeStatus);
- print $gtmessage, "\n";
- if ($TapeMessage == 6) {
- system("/bin/date");
- print "***TarDir: Another damn device reset!! \n";
- print "***TarDir: Tape Driver Failed. \n";
- &ResetTapePosition;
- redo TarLoop;
- # The above oddity due to gnutar not always
- # taking heed of the tape status, and continues
- # merily on it's way, reporting no error,
- # giving the totals properly, but the file not
- # showing up on the tape. Strange.
- }
- if ($gtreturn == 256){
- print "*TarDir: Gnutar reports error $gtreturn \n";
- print "*TarDir: This error does NOT compromise \n";
- print "*TarDir: the archive or the tape. \n";
- print "*TarDir: Gnutar's message: \n @gtmessage \n";
- }
- if ($gtreturn > 256) {
- system("/bin/date");
- sleep (5 * $TarCount++);
- print "@gtmessage \nRunning Time: $tartime seconds \n";
- &Say("*Gnutar reports error $gtreturn");
- &Say("*Repositioning tape and will try again.");
- if ($tartime >= 10) { # was an execution, not an open error.
- &BackSpaceTape(1);
- &EraseRestOfTape;
- &Say("Gnutar ran for $tartime but glitched.", `/bin/date`);
- }
- else {&ResetTapePosition;} #We don't know where we are.
- }
- if ($count >= 3) {
- print "\t**************** Backup Failed. ******************\n";
- print "\t************** Restarting Script ****************\n" ;
- exec($0);
- }
- }until ($gtreturn == 0 );
- for ( @gtmessage ) {
- chop;
- ($T,$b,$w,$Size)=split(/\s+/);
- if ( $Size =~ m/[0-9]+/) {last;}
- }
- chop $gtmessage[0];
- print "Tar of $DirName completed. File $tarnum\n";
- print "Gnutar wrote $Size bytes in $tartime seconds = ";
- print int($Size/$tartime), " B/s \n";
- &Say("Tardir...exited");
- $tabcount--;
- }
- sub VerifyDir {
- $TapeMode=$Read;
- &ForwardSpaceTape(1);
- $tarnum++; print "Tarnum is $tarnum\n";
- # VerifyDir is going to be called most of the time right after
- # a VerifyDir or a RewindTape. In either case, the beginning
- # of the next file to read is an fsf away; as gnutar doesn't
- # read the filemark in the first case, and we have to get
- # past the label in the second case.
- local($BackDir,$FullDirName)=@_;
- local($DirBaseName,$logfile,$count);
- $count = 0;
- $DirName = `basename $FullDirName`;
- chop $DirName;
- $LogLabel = $tapelabel."-".$DirName."-".$DateString;
- $logfile=$BackDir."/".$LogLabel;
- chdir($FullDirName);
- chdir "..";
- do {
- open(LOGFILE,">$logfile");
-
-
- print "Starting Verify of Directory $FullDirName \n" ;
- &TapeReady($reset);
- @message = `$gtar -b 16 +verbose +compare -f $TAPE `;
- $gtreturn = $?;
- if ($gtreturn == 0) {
- open(LOGFILE,">$logfile");
-
- print LOGFILE "$BackupType Backup: File $tarnum ";
- print LOGFILE "on Tape $tapelabel\n";
- print LOGFILE "Directory: $FullDirName\n";
- print LOGFILE "Directory stored as ./$DirName\n";
- print LOGFILE "Date of backup: ",`date`;
- print LOGFILE "Archive size: ",`du -s $FullDirName`;
- print LOGFILE "This file will be deleted when tape is erased.\n";
- print(LOGFILE shift @message);
- @message = sort @message;
- print(LOGFILE @message);
- print "Verification for $DirName complete.\n";
- close(LOGFILE);
- &TarErrors(@message);
- }
- else {
- sleep ($count++);
- print "VerifyDir: gnutar reports error $gtreturn \n";
- &BackSpaceTape(1);
- &ForwardSpaceTape(1);
- close(LOGFILE);
- }
- } until (($gtreturn == 0)||(count > 2));
- if (count > 2) {
- print "\t*************Something is badly screwed up. \n";
- print "\tCut and paste the last few screenfulls into a mail message\n";
- print "\t********to your friendly neighboorhood sysadmin.\n";
- }
- }
- sub TarErrors {
- $count = 0;
- for ( @_ ) {
- if ( /\: .*differs$/ || /\: .*does not exist/ ){$count++;}
- shift;
- }
- if ($count) {
- print "$count ERRORS WERE FOUND DURING THE VERIFY PASS.\n";
- }
- print "Full list is in $logfile.\n";
- }
-
- sub ResetTapePosition {
- $DEBUG = "YES";
- $tabcount++;
- &Say("ResetTapePosition...");
- $reset = -1;
- print "\n********** A Critical Tape Error has occured. **********\n";
- print " Tape will be repositioned to the start of this archive\n";
- print " and the operation will be attempted again.\n";
- print " Please stand by...\n\n";
- #sleep(45);
- #print " Repositioning tape: Tape is rewinding \n";
- Loop:{
- &RewindTape;
- &SetupDrive;
- if (&ForwardSpaceTape($tarnum) != 200) {redo Loop;}
- if ($TapeMode==$Write) {
- if (&BackSpaceTape(1) != 200) {redo Loop;}
- if (&EraseRestOfTape != 200) {redo Loop; }
- }
-
- print "*** Tape repositioning was successful. ***\n";
- print " We now return you to your interupted program. \n";
- $reset = 0;
- }
- &Say("ResetTapePosition...exited");
- $tabcount--;
- }
- ####################################################################
- # sub ErrorTranslate #
- # Sometimes errors are easier to deal with as numbers. #
- # People prefer words. ErrorTransLate prints the associated #
- # error that corresponds to ErrorCode #
- ####################################################################
-
- sub ErrorTranslate {
- local ($code) = $_[0];
- # 100 - 109 Tape Label & permissions errors
- if ($code == 101) {print "Blank Tape. \n";}
- if ($code == 102) {print "Tape label is null string. \n";;}
- if ($code == 103) {print "Label is not registered. \n";}
- if ($code == 104) {print "Not owned by user. \n";}
- # 110 - 120 mt transport errors
- if ($code == 110) {print "***mt failed rewind. Trying again. \n";}
- if ($code == 111) {print "***mt failed writing tapemark ";}
- if ($code == 112) {print "***mt failed advancing tape ";}
- if ($code == 113) {print "***mt failed backspacing tape ";}
- # if ($code == ) {print " ";}
- }
-
- sub Say {
- if ($DEBUG eq "YES") {
- for($i=0;$i<$tabcount;$i++) {print "> ";}
- print (@_,"\n");
- }
- }
- sub Quit {
- $exitcode = @_[0];
- #&Offline;
- exit($exitcode);
- }
- 1;
-
-
-
-